
using System.Collections;
using System.Text;
using KeraLua;

namespace Ex
{
    /// <summary>
    /// 由ExRefTable引申而来的，可以保护数据不受lua端修改的表
    /// *需要lua端禁止(get|set)metatable
    /// </summary>
    public class ExProtectTable : ExRefTable
    {
        private IExETable permit_keyset;

        private int meta_table_ref_id;

        public const string TypeLable = "protecttable";

        public ExProtectTable(EnvLua env, IDictionary? table = null) : base(env, table)
        {
            InitMetatable();
            permit_keyset = new ExRefTable(env);
        }

        /// <summary>
        /// 所得为原表的浅克隆而非引用
        /// </summary>
        /// <param name="table"></param>
        public ExProtectTable(IExETable table) : base(table)
        {
            InitMetatable();
            permit_keyset = new ExRefTable(env);
        }

        /// <summary>
        /// 构造时调用，初始化元表, thread unsafe
        /// </summary>
        /// <remarks>
        /// thread unsafe
        /// </remarks>
        protected void InitMetatable()
        {
            env.Current.NewMetaTable(TypeLable);
            env.Current.PushString("__index");
            env.Current.RawGetInteger(LuaRegistry.Index, RefId);
            env.Current.SetTable(-3);
            env.Current.PushString("__newindex");
            env.Current.PushCFunction(NewIndex);
            env.Current.SetTable(-3);
            meta_table_ref_id = env.Current.Ref(LuaRegistry.Index);
        }


        protected int NewIndex(IntPtr ptr)
        {
            lock (env)
            {
                // table key value
                permit_keyset.PushToLuaStack();
                // table key value permit_table
                // copy key to the top
                env.Current.PushCopy(-3);
                // table key value permit_table copy_key
                env.Current.GetTable(-2);
                // table key value permit_table result|nil
                if (env.Current.IsNil(-1))
                {
                    env.Current.Pop(5);
                    env.Error("permission denied, this table is forbided to revise");
                }
                else
                {
                    env.Current.Pop(2);
                    env.Current.SetTable(-3);
                }
                return 1;
            }
        }


        public void GetMetatable()
        {
            lock (env)
            {
                env.Current.RawGetInteger(LuaRegistry.Index, meta_table_ref_id);
            }
        }

        public void AddPermission(object permit_key)
        {
            lock (env)
            {
                permit_keyset.PushToLuaStack();
                env.PushObjectToLuaStack(permit_key);
                env.Current.PushCopy(-1);
                env.Current.SetTable(-3);
                env.Current.Pop(1);
            }
        }

        public void RemovePermission(object permit_key)
        {
            lock (env)
            {
                permit_keyset.PushToLuaStack();
                env.PushObjectToLuaStack(permit_key);
                env.Current.PushNil();
                env.Current.Pop(1);
            }
        }

        public override void PushToLuaStack()
        {
            lock (env)
            {
                env.Current.NewTable();
                this.GetMetatable();
                env.Current.SetMetaTable(-2);
            }
        }

        ~ExProtectTable()
        {
            lock (env)
            {
                env.Current.Unref(LuaRegistry.Index, meta_table_ref_id);
            }
        }


    }
}